home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / osrc.arc / TCPSUBR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-31  |  8.8 KB  |  400 lines

  1. #include <stdio.h>
  2. #include "global.h"
  3. #include "timer.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "internet.h"
  7. #include "tcp.h"
  8.  
  9. void tcp_timeout(),tcp_msl(),link_tcb();
  10.  
  11. static int16 hash_tcb();    
  12.  
  13. /* TCP connection states */
  14. char *Tcpstates[] = {
  15.     "Closed",
  16.     "Listen",
  17.     "SYN sent",
  18.     "SYN received",
  19.     "Established",
  20.     "FIN wait 1",
  21.     "FIN wait 2",
  22.     "Close wait",
  23.     "Closing",
  24.     "Last ACK",
  25.     "Time wait"
  26. };
  27.  
  28. /* TCP closing reasons */
  29. char *Tcpreasons[] = {
  30.     "Normal",
  31.     "Reset/Refused",
  32.     "Timeout",
  33.     "ICMP"
  34. };
  35. struct tcb *Tcbs[NTCB];
  36. int16 Tcp_mss = DEF_MSS;    /* Maximum segment size to be sent with SYN */
  37. int32 Tcp_irtt = DEF_RTT;    /* Initial guess at round trip time */
  38. int Tcp_trace;            /* State change tracing flag */
  39. struct tcp_rtt Tcp_rtt[RTTCACHE];
  40.  
  41. /* Lookup connection, return TCB pointer or NULLTCB if nonexistant */
  42. struct tcb *
  43. lookup_tcb(conn)
  44. struct connection *conn;
  45. {
  46.     register struct tcb *tcb;
  47.  
  48.     tcb = Tcbs[hash_tcb(conn)];
  49.     while(tcb != NULLTCB){
  50.         /* Yet another structure compatibility hack */
  51.         if(conn->local.address == tcb->conn.local.address
  52.          && conn->remote.address == tcb->conn.remote.address
  53.          && conn->local.port == tcb->conn.local.port
  54.          && conn->remote.port == tcb->conn.remote.port)
  55.             break;
  56.         tcb = tcb->next;
  57.     }
  58.     return tcb;
  59. }
  60.  
  61. /* Create a TCB, return pointer. Return pointer if TCB already exists. */
  62. struct tcb *
  63. create_tcb(conn)
  64. struct connection *conn;
  65. {
  66.     register struct tcb *tcb;
  67.     struct tcp_rtt *tp;
  68.  
  69.     if((tcb = lookup_tcb(conn)) != NULLTCB)
  70.         return tcb;
  71.     if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB)
  72.         return NULLTCB;
  73.     ASSIGN(tcb->conn,*conn);
  74.  
  75.     tcb->cwind = tcb->mss = Tcp_mss;
  76.     tcb->ssthresh = 65535;
  77.     if((tp = rtt_get(tcb->conn.remote.address)) != NULLRTT){
  78.         tcb->srtt = tp->srtt;
  79.         tcb->mdev = tp->mdev;
  80.     } else {
  81.         tcb->srtt = Tcp_irtt;    /* mdev = 0 */
  82.     }
  83.     /* Initialize timer intervals */
  84.     tcb->timer.start = tcb->srtt / MSPTICK;
  85.     tcb->timer.func = tcp_timeout;
  86.     tcb->timer.arg = (char *)tcb;
  87.  
  88.     link_tcb(tcb);
  89.     return tcb;
  90. }
  91.  
  92. /* Close our TCB */
  93. void
  94. close_self(tcb,reason)
  95. register struct tcb *tcb;
  96. int reason;
  97. {
  98.     struct reseq *rp1;
  99.     register struct reseq *rp;
  100.  
  101.     if(tcb == NULLTCB)
  102.         return;
  103.  
  104.     stop_timer(&tcb->timer);
  105.     tcb->reason = reason;
  106.  
  107.     /* Flush reassembly queue; nothing more can arrive */
  108.     for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
  109.         rp1 = rp->next;
  110.         free_p(rp->bp);
  111.         free((char *)rp);
  112.     }
  113.     tcb->reseq = NULLRESEQ;
  114.     setstate(tcb,CLOSED);
  115. }
  116.  
  117. /* Sequence number comparisons
  118.  * Return true if x is between low and high inclusive,
  119.  * false otherwise
  120.  */
  121. int
  122. seq_within(x,low,high)
  123. register int32 x,low,high;
  124. {
  125.     if(low <= high){
  126.         if(low <= x && x <= high)
  127.             return 1;
  128.     } else {
  129.         if(low >= x && x >= high)
  130.             return 1;
  131.     }
  132.     return 0;
  133. }
  134. int
  135. seq_lt(x,y)
  136. register int32 x,y;
  137. {
  138.     return (long)(x-y) < 0;
  139. }
  140. int
  141. seq_le(x,y)
  142. register int32 x,y;
  143. {
  144.     return (long)(x-y) <= 0;
  145. }
  146. int
  147. seq_gt(x,y)
  148. register int32 x,y;
  149. {
  150.     return (long)(x-y) > 0;
  151. }
  152. int
  153. seq_ge(x,y)
  154. register int32 x,y;
  155. {
  156.     return (long)(x-y) >= 0;
  157. }
  158.  
  159. /* Hash a connect structure into the hash chain header array */
  160. static int16
  161. hash_tcb(conn)
  162. struct connection *conn;
  163. {
  164.     register unsigned int hval;
  165.  
  166.     /* Compute hash function on connection structure */
  167.     hval = hiword(conn->remote.address);
  168.     hval ^= loword(conn->remote.address);
  169.     hval ^= hiword(conn->local.address);
  170.     hval ^= loword(conn->local.address);
  171.     hval ^= conn->remote.port;
  172.     hval ^= conn->local.port;
  173.     return hval % NTCB;
  174. }
  175. /* Insert TCB at head of proper hash chain */
  176. void
  177. link_tcb(tcb)
  178. register struct tcb *tcb;
  179. {
  180.     register struct tcb **tcbhead;
  181.     char i_state;
  182.  
  183.     tcb->prev = NULLTCB;
  184.     i_state = dirps();
  185.     tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
  186.     tcb->next = *tcbhead;
  187.     if(tcb->next != NULLTCB)
  188.         tcb->next->prev = tcb;
  189.  
  190.     *tcbhead = tcb;
  191.     restore(i_state);
  192. }
  193. /* Remove TCB from whatever hash chain it may be on */
  194. void
  195. unlink_tcb(tcb)
  196. register struct tcb *tcb;
  197. {
  198.     register struct tcb **tcbhead;
  199.     char i_state;
  200.  
  201.     i_state = dirps();
  202.     tcbhead = &Tcbs[hash_tcb(&tcb->conn)];
  203.     if(tcb->prev == NULLTCB)
  204.         *tcbhead = tcb->next;    /* We're the first one on the chain */
  205.     else
  206.         tcb->prev->next = tcb->next;
  207.     if(tcb->next != NULLTCB)
  208.         tcb->next->prev = tcb->prev;
  209.     restore(i_state);
  210. }
  211. void
  212. setstate(tcb,newstate)
  213. register struct tcb *tcb;
  214. register int newstate;
  215. {
  216.     register char oldstate;
  217.  
  218.     oldstate = tcb->state;
  219.     tcb->state = newstate;
  220.     if(Tcp_trace)
  221.         printf("TCB %lx %s -> %s\n",ptol(tcb),
  222.          Tcpstates[oldstate],Tcpstates[newstate]);
  223.     if(tcb->s_upcall)
  224.         (*tcb->s_upcall)(tcb,oldstate,newstate);
  225.  
  226.     switch(newstate){
  227.     case ESTABLISHED:
  228.         /* Notify the user that he can begin sending data */
  229.         if(tcb->t_upcall)
  230.             (*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
  231.         break;
  232.     }
  233. }
  234. /* Convert TCP header in host format into mbuf ready for transmission,
  235.  * link in data (if any), and compute checksum
  236.  */
  237. struct mbuf *
  238. htontcp(tcph,data,ph)
  239. register struct tcp *tcph;
  240. struct mbuf *data;
  241. struct pseudo_header *ph;
  242. {
  243.     int16 hdrlen;
  244.     struct mbuf *bp;
  245.     register char *cp;
  246.     int16 csum;
  247.  
  248.     hdrlen = (tcph->mss != 0) ? TCPLEN + MSS_LENGTH : TCPLEN;
  249.     
  250.     if((bp = pushdown(data,hdrlen)) == NULLBUF){
  251.         free_p(data);
  252.         return NULLBUF;
  253.     }
  254.     cp = bp->data;
  255.     cp = put16(cp,tcph->source);
  256.     cp = put16(cp,tcph->dest);
  257.     cp = put32(cp,tcph->seq);
  258.     cp = put32(cp,tcph->ack);
  259.     *cp++ = hdrlen << 2;    /* Offset field */
  260.     *cp = 0;
  261.     if(tcph->flags.urg)
  262.         *cp |= 32;
  263.     if(tcph->flags.ack)
  264.         *cp |= 16;
  265.     if(tcph->flags.psh)
  266.         *cp |= 8;
  267.     if(tcph->flags.rst)
  268.         *cp |= 4;
  269.     if(tcph->flags.syn)
  270.         *cp |= 2;
  271.     if(tcph->flags.fin)
  272.         *cp |= 1;
  273.     cp++;
  274.     cp = put16(cp,tcph->wnd);
  275.     *cp++ = 0;    /* Zero out checksum field */
  276.     *cp++ = 0;
  277.     cp = put16(cp,tcph->up);
  278.  
  279.     if(tcph->mss != 0){
  280.         *cp++ = MSS_KIND;
  281.         *cp++ = MSS_LENGTH;
  282.         cp = put16(cp,tcph->mss);
  283.     }
  284.     csum = cksum(ph,bp,ph->length);
  285.     /* Fill checksum field */    
  286.     put16(&bp->data[16],csum);
  287.  
  288.     return bp;
  289. }
  290. /* Pull TCP header off mbuf */
  291. int
  292. ntohtcp(tcph,bpp)
  293. register struct tcp *tcph;
  294. struct mbuf **bpp;
  295. {
  296.     int16 hdrlen;
  297.     int16 i,optlen;
  298.     register int flags;
  299.     char hdrbuf[TCPLEN];
  300.  
  301.     i = pullup(bpp,hdrbuf,TCPLEN);
  302.     /* Note that the results will be garbage if the header is too short.
  303.      * We don't check for this because returned ICMP messages will be
  304.      * truncated, and we at least want to get the port numbers.
  305.      */
  306.     tcph->source = get16(&hdrbuf[0]);
  307.     tcph->dest = get16(&hdrbuf[2]);
  308.     tcph->seq = get32(&hdrbuf[4]);
  309.     tcph->ack = get32(&hdrbuf[8]);
  310.     hdrlen = (hdrbuf[12] & 0xf0) >> 2;
  311.     flags = hdrbuf[13];
  312.     tcph->flags.urg = flags & 32;
  313.     tcph->flags.ack = flags & 16;
  314.     tcph->flags.psh = flags & 8;
  315.     tcph->flags.rst = flags & 4;
  316.     tcph->flags.syn = flags & 2;
  317.     tcph->flags.fin = flags & 1;
  318.     tcph->wnd = get16(&hdrbuf[14]);
  319.     tcph->up = get16(&hdrbuf[18]);
  320.     tcph->mss = 0;
  321.  
  322.     /* Check for option field. Only space for one is allowed, but
  323.      * since there's only one TCP option (MSS) this isn't a problem
  324.      */
  325.     if(i < TCPLEN || hdrlen < TCPLEN)
  326.         return -1;    /* Header smaller than legal minimum */
  327.     if(hdrlen == TCPLEN)
  328.         return hdrlen;    /* No options, all done */
  329.  
  330.     if(hdrlen > len_mbuf(*bpp) + TCPLEN){
  331.         /* Remainder too short for options length specified */
  332.         return -1;
  333.     }
  334.     /* Process options */
  335.     for(i=TCPLEN; i < hdrlen;){
  336.         switch(pullchar(bpp)){
  337.         case EOL_KIND:
  338.             i++;
  339.             goto eol;    /* End of options list */
  340.         case NOOP_KIND:
  341.             i++;
  342.             break;
  343.         case MSS_KIND:
  344.             optlen = pullchar(bpp);
  345.             if(optlen == MSS_LENGTH)
  346.                 tcph->mss = pull16(bpp);
  347.             i += optlen;
  348.             break;
  349.         }
  350.     }
  351. eol:
  352.     /* Get rid of any padding */
  353.     if(i < hdrlen)
  354.         pullup(bpp,NULLCHAR,hdrlen - i);
  355.     return hdrlen;
  356. }
  357. /* Round trip timing cache routines.
  358.  * These functions implement a very simple system for keeping track of
  359.  * network performance for future use in new connections.
  360.  * The emphasis here is on speed of update (rather than optimum cache hit
  361.  * ratio) since rtt_add is called every time a TCP connection updates
  362.  * its round trip estimate.
  363.  */
  364. void
  365. rtt_add(addr,rtt)
  366. int32 addr;        /* Destination IP address */
  367. int32 rtt;
  368. {
  369.     register struct tcp_rtt *tp;
  370.     int32 abserr;
  371.  
  372.     if(addr == 0)
  373.         return;
  374.     tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
  375.     if(tp->addr != addr){
  376.         /* New entry */
  377.         tp->addr = addr;
  378.         tp->srtt = rtt;
  379.         tp->mdev = 0;
  380.     } else {
  381.         /* Run our own SRTT and MDEV integrators, with rounding */
  382.         abserr = (rtt > tp->srtt) ? rtt - tp->srtt : tp->srtt - rtt;
  383.         tp->srtt = ((AGAIN-1)*tp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
  384.         tp->mdev = ((DGAIN-1)*tp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
  385.     }
  386. }
  387. struct tcp_rtt *
  388. rtt_get(addr)
  389. int32 addr;
  390. {
  391.     register struct tcp_rtt *tp;
  392.  
  393.     if(addr == 0)
  394.         return NULLRTT;
  395.     tp = &Tcp_rtt[(unsigned short)addr % RTTCACHE];
  396.     if(tp->addr != addr)
  397.         return NULLRTT;
  398.     return tp;
  399. }
  400.